"""
分布式问题
    1.分布式事务问题   本地事务不支持跨库操作

        方案1:
            将有关联的表放在一个数据库中
            同库操作可以使用一个事务
            如用户表&用户频道表, 文章基本信息表&文章内容表放在一起

        方案2:
            核心是二阶段提交协议(简称 2PC协议 / XA协议)
            分布式事务会提供一个 事务管理器 来对 各数据库的本地事务进行统一管理, 只有各本地事务都向管理器 预提交 成功后, 事务管理器才会统一执行提交处理, 否则统一进行回滚处理
            sqlalchemy 也支持分布式事务
                只需要在创建 SQLAlchemy对象时, 设置参数 session_options={'twophase': True}即可
                设置后, 整个session的所有操作会被放入到一个分布式事务中, 并在整个分布式事务范围内保证原子性

        方案3:
            基于状态/消息的最终一致性方案
            对于 包含多个子系统的大型项目, 需要保证子系统之间的数据一致性
            单个子系统往往不会操作所有数据库, 但是 每个子系统可以通过定义字段来记录操作的状态, 每完成一个阶段则更新相应的状态
            如下单-付款流程中, 应用A的下单事务完成后更新订单状态为已下单, 应用B付款事务完成后, 再通过 支付回调接口 通知应用A 更新订单状态
            应用B还需要提供一个 支付查询接口, 以便在用户查询或者订单超时的情况下, 让应用A可以查询订单的支付情况
            ebay提出的方案, 理论叫做BASE

        注意点:
            分布式事务要在所有事务都"提交成功"的情况下才会正式提交, 如果参与的部分节点卡顿, 会影响整个事务的性能


    2. 跨节点 Join/排序/分页   不支持的跨库操作包括join/分组/聚合/排序
        方案1:
            分两次查询进行, 在应用端合并

        方案2:
            使用一些第三方方案(数据库中间件)
            开源框架除了Mycat, 功能较少
            需要一定学习成本, 二次开发需要公司具有一定技术实力

            付费框架推荐: 阿里DRDS
                功能: 分库分表、分布式JOIN/聚合/排序、分布式事务、平滑扩容、读写分离, 全局唯一ID
                基础版: 14000+/年
                一个字: 强!
"""
# 1.分布式事务问题 方案2
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 设置多个数据库地址
app.config['SQLALCHEMY_BINDS'] = {
    'db1': 'mysql://root:mysql@192.168.19.137:3306/db1',
    'db2': 'mysql://root:mysql@192.168.19.137:3306/db2'
}

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = False

"""实际开发中, 能用本地事务尽量使用本地事务, 使用db2, 如果特殊情况下, 需要使用分布式事务, 再通过db进行数据操作"""
# 创建组件对象  设置session_options={'twophase': True},则会开启分布式事务, 核心二阶段提交协议
db2 = SQLAlchemy(app)
db = SQLAlchemy(app, session_options={'twophase': True})


# 用户表
class User(db.Model):
    __tablename__ = 't_user'
    __bind_key__ = 'db1'  # 设置数据库db1
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20))


# 地址表
class Address(db.Model):
    __tablename__ = 't_adr'
    __bind_key__ = 'db2'  # 设置数据库db2
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20), unique=True)
    user_id = db.Column(db.Integer)


@app.route('/')
def index():
    """添加数据"""
    user1 = User(name='张三')
    db.session.add(user1)
    db.session.flush()

    adr1 = Address(detail='中关村3号', user_id=user1.id)
    adr2 = Address(detail='华强北5号', user_id=user1.id)
    db.session.add_all([adr1, adr2])
    db.session.flush()

    db.session.commit()  # 由于采用了分布式事务, 整个session的操作会被放入到一个分布式事务中, 并实现事务的原子性
    return "index"


@app.route('/demo1')
def demo1():
    """查询多表数据  需求: 查询姓名为"张三"的所有地址信息"""

    # 先根据姓名查找用户主键
    user1 = User.query.filter_by(name='张三').first()

    # 再根据主键到从表查询关联地址
    adrs = Address.query.filter_by(user_id=user1.id).all()
    for adr in adrs:
        print(adr.detail)

    return 'demo1'


if __name__ == '__main__':
    # 删除所有继承自db.Model的表
    db.drop_all()
    # 创建所有继承自db.Model的表
    db.create_all()

    app.run(debug=True)
